﻿Imports LibMultiPlug.Core.Plugins.Interfaces
Imports LibMultiPlug.Core.Configuration

Namespace Plugins

    Public Class PluginManager

#Region "SingleTon Implementation"


        ''' <summary>
        ''' Verwaltet die einmalige Instanz des PluginManager
        ''' </summary>
        Private Shared _instance As PluginManager


        ''' <summary>
        ''' Steuert das Erstellen des PluginManager-Instanz und liefert sie.
        ''' </summary>
        ''' <returns>Die einmalige Instanz des PluginManagers</returns>
        Public Shared Function GetInstance() As PluginManager

            ' wenn die Instanz noch nicht existiert, wird sie erstellt.
            If PluginManager._instance Is Nothing Then _
                PluginManager._instance = New PluginManager

            Return PluginManager._instance

        End Function

#End Region


#Region "Member"


        ''' <summary>
        ''' Liste der geladenen Plugins.
        ''' </summary>
        ''' <remarks></remarks>
        Private _plugins As List(Of Interfaces.IPlugV1)


        ''' <summary>
        ''' Schalter der angibt, ob die Plugins schon geladen sind.
        ''' </summary>
        ''' <remarks></remarks>
        Private _loaded As Boolean


        ''' <summary>
        ''' Log4net log Instanz
        ''' </summary>
        ''' <remarks></remarks>
        Private _logger As Global.log4net.ILog


        ''' <summary>
        ''' Die Liste der geladenen Plugindefinitionen
        ''' </summary>
        Private _pluginDefinitions As List(Of PluginElement)


        ''' <summary>
        ''' Legt die Bezeichnung für die Konfiguration in der app.config fest.
        ''' </summary>
        ''' <remarks></remarks>
        Private MULTIPLUG_PLUGINGS_CONFIGSECTION As String = "multiPlugPlugins"


#End Region


#Region "Events"


        ''' <summary>
        ''' Event, das augelöst wird, wenn das Laden eines Plugins fehlgeschlagen ist.
        ''' </summary>
        ''' <param name="pluginDefinition">Die Definition des Plugins, bei dem der Fehler aufgetreten ist.</param>
        ''' <param name="exception">Der Fehler der aufgetreten ist.</param>
        ''' <param name="continueLoading">Legt fest, ob das laden der andern Plugins fortgesetzt werden soll.</param>
        Public Event PluginLoadingFailed(ByVal pluginDefinition As PluginElement, _
                                         ByVal exception As Exception, _
                                         ByRef continueLoading As Boolean)


        ''' <summary>
        ''' Meldet ein erfolgreich geladenes Plugin.
        ''' </summary>
        ''' <param name="plugin">Das erfolgreich geladene Plugin</param>
        Public Event PluginLoaded(ByVal plugin As Interfaces.IPlugV1)


#End Region


#Region "Properties"


        ''' <summary>
        ''' Die Sammlung der Umgebungsdaten, die an das Plugin übergeben werden.
        ''' </summary>
        Public Property Environment() As PluginEnvironment


        ''' <summary>
        ''' Gibt an ob die Plugins geladen sind.
        ''' </summary>
        Public ReadOnly Property Loaded As Boolean
            Get
                Return _loaded
            End Get
        End Property


        ''' <summary>
        ''' Liefert alle geladenen Plugins.
        ''' </summary>
        ''' <value></value>
        Public ReadOnly Property Plugins As List(Of Interfaces.IPlugV1)
            Get
                Return _plugins
            End Get
        End Property


#End Region


        Private Sub New()

            _logger = Global.log4net.LogManager.GetLogger(GetType(PluginManager))
            _plugins = New List(Of Interfaces.IPlugV1)
            _loaded = False

        End Sub


#Region "Methods"


        Public ReadOnly Property LoadedPlugins As List(Of Interfaces.IPlugV1)
            Get
                Return _plugins
            End Get
        End Property


        Private Sub LoadFromConfiguration()

            Dim mppc As MultiPlugPluginsConfig = _
                CType(System.Configuration.ConfigurationManager.GetSection(MULTIPLUG_PLUGINGS_CONFIGSECTION),  _
                      MultiPlugPluginsConfig)

            _pluginDefinitions = PluginCollection.ToList(mppc.Plugins)

            _pluginDefinitions.Sort()

        End Sub


        Public Function LoadAll() As List(Of Interfaces.IPlugV1)

            _plugins.Clear()

            Call LoadFromConfiguration()

            For Each currentPlugin As PluginElement In _pluginDefinitions

                Try

                    ' laden des assemblies
                    Dim pluginAssembly As System.Reflection.Assembly = _
                        System.Reflection.Assembly.LoadFrom(currentPlugin.Assembly)

                    ' laden des plugins anhand seines typs aus der plugindefinition 
                    _plugins.Add(CType(System.Activator.CreateInstance(pluginAssembly.GetType(currentPlugin.EntryType)),  _
                                       Interfaces.IPlugV1))

                    If _logger.IsDebugEnabled Then _
                        _logger.DebugFormat("Plugin {0} hinzugefügt...", LoadedPlugins(LoadedPlugins.Count - 1).Name)

                    ' über ein event melden, das ein plugin erfolgreich geladen wurde
                    RaiseEvent PluginLoaded(LoadedPlugins(LoadedPlugins.Count - 1))

                Catch ex As Exception

                    ' schalter, ob die nächsten plugins geladen werden sollen
                    Dim continueLoading As Boolean = True

                    If _logger.IsErrorEnabled Then _
                        _logger.Error(String.Format("Fehler beim Hinzufügen des Plugins {0}.", currentPlugin.EntryType), ex)

                    ' das event auslösen, das auf einen fehler beim laden hinweist
                    RaiseEvent PluginLoadingFailed(currentPlugin, ex, continueLoading)

                    ' wen nicht fortgefahren werden soll, die forschleife beenden
                    If Not continueLoading Then _
                        Exit For

                End Try

            Next

            _loaded = True

            Call InitializePlugins()

            Return LoadedPlugins

        End Function


        Private Sub InitializePlugins()

            ' für jedes geladenen plugin die initialize methode 
            ' aufrufen und die daten "von hier" übergeben
            For Each plugin In LoadedPlugins

                Try

                    ' das plugin initialisieren
                    Call plugin.Initialize(Environment)

                    If plugin.IsInitialized AndAlso _logger.IsDebugEnabled Then _
                            _logger.DebugFormat("Plugin {0} initialisiert...", LoadedPlugins(LoadedPlugins.Count).Name)


                    ' wenn das plugin nicht installiert ist, dann wird das gemacht.
                    If Not plugin.IsInstalled Then

                        Try

                            Call plugin.Install()

                        Catch ex As Exception

                            If _logger.IsErrorEnabled Then _
                                _logger.Error(String.Format("Fehler beim installieren des Plugins {0} ...", LoadedPlugins(LoadedPlugins.Count).Name), ex)

                        End Try

                    End If

                Catch ex As Exception

                    Try
                        If _logger.IsErrorEnabled Then _
                            _logger.Error(String.Format("Fehler beim initialisieren des Plugins {0} ...", LoadedPlugins(LoadedPlugins.Count).Name), ex)
                    Catch innerEx As Exception

                    End Try

                End Try


            Next

        End Sub


        Public Function StartTransaktion(ByVal transaktionName As String, ByVal args() As String) As Boolean

            For Each plugin In LoadedPlugins

                StartTransaktion = InternalStartTransaktion(plugin, transaktionName, args)

                If StartTransaktion Then _
                    Exit Function

            Next

            Return False

        End Function


        Private Function InternalStartTransaktion(ByVal plugin As IPlugChildV1, ByVal transaktionName As String, ByVal args() As String) As Boolean

            If plugin.Transaktion IsNot Nothing AndAlso plugin.Transaktion.ToLower().Equals(transaktionName.ToLower()) Then
                Call plugin.StartTransaction(args)
                Return True
            End If

            If plugin.Children IsNot Nothing Then

                For Each childPlugin In plugin.Children

                    If InternalStartTransaktion(childPlugin, transaktionName, args) Then _
                        Return True
                Next

            End If

            Return False

        End Function


#End Region


    End Class

End Namespace
